/*
 * Copyright (c) 2022-2023 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef OHOS_FILEMGMT_BACKUP_B_REPORT_ENTITY_H
#define OHOS_FILEMGMT_BACKUP_B_REPORT_ENTITY_H

#include <any>
#include <fcntl.h>
#include <map>
#include <string>
#include <unordered_map>
#include <vector>

#include "b_error/b_error.h"
#include "filemgmt_libhilog.h"
#include "unique_fd.h"

namespace OHOS::FileManagement::Backup {
using namespace std;
struct ReportFileInfo {
    string filePath {""};
    string mode {""};
    bool isDir {false};
    off_t size {0};
    off_t mtime {0};
    string hash {""};
    bool isIncremental {false};
};

class BReportEntity {
public:
    std::vector<std::string> SplitStringByChar(const std::string &str, const char &sep)
    {
        std::vector<std::string> eles = {};
        std::stringstream ss = std::stringstream(str);
        std::string ele = {};
        while (!ss.eof()) {
            getline(ss, ele, sep);
            eles.push_back(ele);
        }

        return eles;
    }

    ErrCode ParseReportInfo(struct ReportFileInfo &fileStat, const std::vector<std::string> &splits,
                            const std::unordered_map<std::string, int> &keys)
    {
        // 根据数据拼接结构体
        int len = keys.size();
        int splitsLen = (int)splits.size();
        // 处理path路径
        std::string path;
        std::vector<std::string> residue;
        HILOGE("ParseReportInfo start splitsLen=%{public}d, len=%{public}d", splitsLen, len);
        try {
            for (int i = 0; i < splitsLen; i++) {
                if (i <= splitsLen - len) {
                    path += splits[i] + ";";
                } else {
                    HILOGE("ParseReportInfo split %{public}s", splits[i].data());
                    residue.emplace_back(splits[i]);
                }
            }
            fileStat.filePath = path.substr(0, path.length() - 1);
            fileStat.mode = residue[keys.find("mode")->second];
            fileStat.isDir = atoi(residue[keys.find("dir")->second].c_str()) == 1 ? true : false;
            fileStat.size = static_cast<off_t>(atol(residue[keys.find("size")->second].c_str()));
            fileStat.mtime = static_cast<off_t>(atol(residue[keys.find("mtime")->second].c_str()));
            fileStat.hash = residue[keys.find("hash")->second];
            if (keys.find("isIncremental") != keys.end()) {
                fileStat.isIncremental = atoi(residue[keys.find("isIncremental")->second].c_str()) == 1 ? true : false;
            }
            HILOGE("ParseReportInfo end");
            return ERR_OK;
        } catch (...) {
            HILOGE("Failed to ParseReportInfo");
            return EPERM;
        } 
    }

    void DealLine(std::unordered_map<string, int> &keys, int &num, const std::string &line,
        std::map<std::string, struct ReportFileInfo> &infos)
    {
        std::string currentLine = line;
        if (currentLine[currentLine.length()-1] == '\r') {
            currentLine = currentLine.substr(0, currentLine.length()-1);
        }

        HILOGE("DealLine begin:%{public}s", currentLine.c_str());
        std::vector<std::string> splits = SplitStringByChar(currentLine, attrSep_);
        HILOGE("DealLine end");
        if (num < INFO_ALIGN_NUM_) {
            if (num == 1) {
                for (int j = 0; j < (int)splits.size(); j++) {
                    HILOGE("keys %{public}s, j=%{public}d", splits[j].data(), j);
                    keys.emplace(splits[j], j - 1);
                }
            }
            num++;
        } else {
            HILOGE("ParseReportInfo begin:%{public}s", currentLine.c_str());
            struct ReportFileInfo fileState;
            for (int j = 0; j < (int)splits.size(); j++) {
                HILOGE("splits %{public}s, j=%{public}d", splits[j].data(), j);
            }
            auto code = ParseReportInfo(fileState, splits, keys);
            if (code != ERR_OK) {
                HILOGE("ParseReportInfo err:%{public}d, %{public}s", code, currentLine.c_str());
            } else {
                infos.try_emplace(fileState.filePath, fileState);
            }
            HILOGE("ParseReportInfo end");
        }
    }

    /**
     * @brief 获取Report信息
     *
     * @return std::map<string, ReportFileInfo>
     */
    std::map<std::string, struct ReportFileInfo> GetReportInfos()
    {
        std::map<std::string, struct ReportFileInfo> infos {};

        char buffer[HASH_BUFFER_SIZE_];
        ssize_t bytesRead;
        std::string currentLine;
        std::unordered_map<string, int> keys;

        int num = 0;
        while ((bytesRead = read(srcFile_, buffer, sizeof(buffer))) > 0) {
            HILOGI("GetReportInfos line:%{public}s", buffer);
            for (ssize_t i = 0; i < bytesRead; i++) {
                if (buffer[i] == lineSep_) {
                    HILOGI("GetReportInfos line:%{public}s", currentLine.c_str());
                    DealLine(keys, num, currentLine, infos);
                    HILOGI("GetReportInfos  end");
                    currentLine.clear();
                } else {
                    currentLine += buffer[i];
                }
            }
        }
        
        // 处理文件中的最后一行
        if (!currentLine.empty()) {
            HILOGI("GetReportInfos2 line:%{public}s", currentLine.c_str());
            DealLine(keys, num, currentLine, infos);
            HILOGI("GetReportInfos2 end");
        }

        return infos;
    }

public:
    /**
     * @brief 构造方法
     *
     * @param fd
     */
    explicit BReportEntity(UniqueFd fd) : srcFile_(std::move(fd)) {}

    BReportEntity() = delete;
    virtual ~BReportEntity() = default;

public:
    string version = "";
    unsigned int attrNum = 0;

protected:
    UniqueFd srcFile_;
    const char lineSep_ = '\n';
    const char attrSep_ = ';';
    const char infoSep_ = '&';
    const char infoAlign_ = '=';
    const int INFO_ALIGN_NUM_ = 2;
    const string VERSION_TAG_ = "version";
    const string ATTR_NUM_TAG_ = "attrNum";
    const int64_t HASH_BUFFER_SIZE_ = 4096; // 每次读取的siz
};
} // namespace OHOS::FileManagement::Backup

#endif // OHOS_FILEMGMT_BACKUP_B_REPORT_ENTITY_H